// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
// SPDX-License-Identifier: MIT
package exporter

import (
    "os"
    "fmt"
    "time"
    "testing"
    "path/filepath"
    "uos-dovecot-exporter/pkg/logger"

    "gopkg.in/yaml.v2"
    "github.com/alecthomas/kingpin"
    "github.com/sirupsen/logrus"
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/require"
)

func TestMain(m *testing.M) {
    tmpDir, err := os.MkdirTemp("", "config_test")
    if err != nil {
        logrus.Fatalf("Failed to create temp dir: %v", err)
    }
    defer os.RemoveAll(tmpDir)

    code := m.Run()
    os.Exit(code)
}

func TestDefaultConfig(t *testing.T) {
    defaultCfg := DefaultConfig

    assert.Equal(t, "0.0.0.0", defaultCfg.Address)
    assert.Equal(t, 9107, defaultCfg.Port)
    assert.Equal(t, "/metrics", defaultCfg.MetricsPath)
    assert.Equal(t, "debug", defaultCfg.Logging.Level)
    assert.Equal(t, "/var/log/uos-exporter/uos-dovecot-exporter.log", defaultCfg.Logging.LogPath)
    assert.Equal(t, "10MB", defaultCfg.Logging.MaxSize)
    assert.Equal(t, time.Hour*24*7, defaultCfg.Logging.MaxAge)
}

func TestUnpack_FileNotExists(t *testing.T) {
    nonExistentFile := "/non/existent/config.yaml"
    Configfile = &nonExistentFile

    var config Config
    err := Unpack(&config)

    assert.NoError(t, err)
}

func TestUnpack_InvalidYAML(t *testing.T) {
    tmpDir := t.TempDir()
    configFile := filepath.Join(tmpDir, "invalid_config.yaml")

    invalidYAML := `invalid: yaml: content: [`
    err := os.WriteFile(configFile, []byte(invalidYAML), 0644)
    require.NoError(t, err)

    Configfile = &configFile

    var config Config
    err = Unpack(&config)

    assert.Error(t, err)
    assert.Contains(t, err.Error(), "decode YAML config")
}

func TestUnpack_ValidConfig(t *testing.T) {
    tmpDir := t.TempDir()
    configFile := filepath.Join(tmpDir, "valid_config.yaml")

    validConfig := Config{
        Logging: logger.Config{
            Level:   "info",
            LogPath: "/tmp/test.log",
            MaxSize: "5MB",
            MaxAge:  time.Hour * 24,
        },
        Address:     "127.0.0.1",
        Port:        8080,
        MetricsPath: "/custom-metrics",
    }

    data, err := yaml.Marshal(validConfig)
    require.NoError(t, err)

    err = os.WriteFile(configFile, data, 0644)
    require.NoError(t, err)

    Configfile = &configFile

    var loadedConfig Config
    err = Unpack(&loadedConfig)

    assert.NoError(t, err)
    assert.Equal(t, validConfig, loadedConfig)
}

func TestUnpack_PermissionDenied(t *testing.T) {
    if os.Getenv("GOOS") == "windows" {
        t.Skip("Skipping permission test on Windows")
    }

    tmpDir := t.TempDir()
    configFile := filepath.Join(tmpDir, "restricted_config.yaml")

    err := os.WriteFile(configFile, []byte(""), 0000)
    require.NoError(t, err)

    Configfile = &configFile

    var config Config
    err = Unpack(&config)

    assert.Error(t, err)
    assert.Contains(t, err.Error(), "decode YAML config: EOF")
}

func TestUnpack_NilConfig(t *testing.T) {
    tmpDir := t.TempDir()
    configFile := filepath.Join(tmpDir, "test_config.yaml")

    err := os.WriteFile(configFile, []byte("address: test"), 0644)
    require.NoError(t, err)

    Configfile = &configFile

    defer func() {
        if r := recover(); r != nil {
            t.Log("Recovered from expected panic:", r)
        }
    }()

    err = Unpack(nil)
    if err != nil {
        assert.Contains(t, err.Error(), "invalid config")
    }
}

func TestUnpack_PartialConfig(t *testing.T) {
    tmpDir := t.TempDir()
    configFile := filepath.Join(tmpDir, "partial_config.yaml")

    partialConfig := `address: "192.168.1.1"
port: 9090`

    err := os.WriteFile(configFile, []byte(partialConfig), 0644)
    require.NoError(t, err)

    Configfile = &configFile

    var config Config
    err = Unpack(&config)

    assert.NoError(t, err)
    assert.Equal(t, "192.168.1.1", config.Address)
    assert.Equal(t, 9090, config.Port)
    assert.Equal(t, "", config.MetricsPath)
}

func TestInit_CommandLineFlags(t *testing.T) {
    oldArgs := os.Args
    defer func() { os.Args = oldArgs }()

    os.Args = []string{
        "test",
        "-c", "/test/config.yaml",
        "-h",
    }

    assert.NotPanics(t, func() {
        kingpin.CommandLine = kingpin.New("test", "Test application")
        kingpin.HelpFlag.Short('h')
        testConfigfile := kingpin.Flag("config", "Configuration file").
            Short('c').
            Default("/etc/uos-exporter/uos-dovecot-exporter.yaml").
            String()

        assert.NotNil(t, testConfigfile)
    })
}

func BenchmarkUnpack(b *testing.B) {
    tmpDir := b.TempDir()
    configFile := filepath.Join(tmpDir, "benchmark_config.yaml")

    config := Config{
        Address:     "localhost",
        Port:        9107,
        MetricsPath: "/metrics",
    }

    data, err := yaml.Marshal(config)
    require.NoError(b, err)

    err = os.WriteFile(configFile, data, 0644)
    require.NoError(b, err)

    Configfile = &configFile

    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        var cfg Config
        _ = Unpack(&cfg)
    }
}

func TestUnpack_EmptyConfig(t *testing.T) {
    tmpDir := t.TempDir()
    configFile := filepath.Join(tmpDir, "empty_config.yaml")

    err := os.WriteFile(configFile, []byte(""), 0644)
    require.NoError(t, err)

    Configfile = &configFile

    var config Config
    err = Unpack(&config)

    assert.Equal(t, Config{}, config)
}

func ExampleUnpack() {
    var cfg Config

    cfg = DefaultConfig

    if err := Unpack(&cfg); err != nil {
        logrus.Errorf("Failed to load config: %v", err)
        return
    }

    fmt.Printf("Server running on %s:%d%s\n",
        cfg.Address, cfg.Port, cfg.MetricsPath)
}
